package com.br.aiops.counter;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.br.aiops.events.CountEvent;
import com.br.aiops.receiver.AlertLog;
import com.br.aiops.rule.Rule;
import com.br.aiops.rule.RulePattern;
import org.apache.commons.collections4.CollectionUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * 统计所有的日志信息
 * 对于匹配到规则的日志，则进行统计
 */
@Component
public class Counter {

    private static final Logger LOGGER = LoggerFactory.getLogger(Counter.class);

    @Autowired
    private RulePattern pattern;

    @Resource
    private ApplicationEventPublisher applicationEventPublisher;

    /**
     * 统计数据结构：
     *
     *   Map<String, AlertLogCounter>
     *      [service]_errorCode_ruleId: AlertLogCounter
     *                               alertLog：AlertLog
     *                               startTime：Date
     *                               updateTime：Date
     *                               rule
     *                               count(alertLog)
     *                               shouldAlert() //是否触发
     *                               isResume() //是否恢复
     *                               shouldLadderAlert() //是否需要阶梯告警，需要则使用阶梯调度器进行调度判断发送告警；否则根据当前状态判断是否触发告警，如果是，则发送告警； 判断后天就清除监听器
     */
    private static final Map<String, AlertLogCounter> counter = new ConcurrentHashMap<>();

    private Lock lock = new ReentrantLock();
    private Condition condition = lock.newCondition();

    private static volatile boolean isLoadReady = false;

    /**
     * 对alertlog进行统计，将值存放到counter中
     */
    public void count(AlertLog alertLog) {
        //等待持久化加载完成
        if (!isLoadReady) {
            waitLoadReady();
        }

        List<Rule> rules = pattern.patten(alertLog);
        if (CollectionUtils.isEmpty(rules)) {
            LOGGER.info("该log没有匹配到规则，不进行统计：{}", alertLog);
            return;
        }

        rules.forEach(rule -> {
            AlertLogCounter newAlertLogCounter = AlertLogCounter.builder()
                    .alertLog(alertLog)
                    .rule(rule)
                    .alertTimes(new AtomicInteger())
                    .alertTimesInAllCycle(new AtomicInteger())
                    .pods(new HashSet<>())
                    .hosts(new HashSet<>())
                    .build();

            String key = newAlertLogCounter.counterKey();

            if (newAlertLogCounter.shouldLadderAlert()) {
                CounterLadderValue.setLadderValueIfAbsent(key, rule);
            }

            try {
                lock.lock();

                counter.putIfAbsent(key, newAlertLogCounter);
                AlertLogCounter alertLogCounter = counter.get(key);
                alertLogCounter.count(alertLog);
                AlertLogCounter copy = JSONObject.parseObject(JSON.toJSONString(alertLogCounter), AlertLogCounter.class);
                //推送事件
                applicationEventPublisher.publishEvent(new CountEvent(copy));
            } finally {
                lock.unlock();
            }
        });
    }

    private void waitLoadReady() {
        try {
            lock.lock();
            while (!isLoadReady){
                try {
                    condition.await(1, TimeUnit.SECONDS);
                } catch (InterruptedException e) {
                    LOGGER.error("线程中断异常：", e);
                    Thread.currentThread().interrupt();
                }
            }
        } finally {
            lock.unlock();
        }
    }

    public Map<String, AlertLogCounter> getCounter() {
        return counter;
    }

    public void ready() {
        try {
            lock.lock();
            isLoadReady = true;
            condition.signalAll();
        } finally {
            lock.unlock();
        }
    }
}
